Vue.prototype.$watch = function (exportFn, cb, options) {
  const vm = this;
  options = options || {}
  const watcher = new Watcher(vm, exportFn, cb, options)
  if (options.immediate) {
    cb.call(vm, watcher.value)
  }
  return function unwatch() {
    watcher.teardown()
  }
}



export class Watcher {
  constructor(vm, exportFn, cb) {
    this.vm = vm;
    this.deps = []  //存储订阅过的dep
    this.depIds = new Set()   //存储订阅过dep的id
    // 执行this.getter。可以读取到data.a.b.c的内容
    if (typeof exportFn == 'function') {
      // 如果是函数直接给getter   不是的话就读取keypath中数据。
      // 当exportFn是函数时，Watch会观察函数中所有用到的vue实例中的响应式数据。函数从vue实例读取了两个响应式数据，那么watcher会观察这两个数据的变化。
      this.getter = exportFn
    } else {
      this.getter = parsePath(exportFn);
    }
    this.cb = cb;
    this.value = this.get();
  }
  get() {
    window.target = this;
    let value = this.getter.call(this.vm, this.vm);
    window.target = undefined;
    return value;
  }
  update() {
    const oldValue = this.value;
    this.value = this.get();
    this.cb.call(this.vm, this.value, oldValue);
  }
  // 记录订阅过哪些dep
  addDep(dep) {
    const id = dep.id;
    if (!this.deps.has(id)) {
      this.depIds.push(id)
      this.deps.push(dep)
      dep.addSub(this)
    }
  }
  /**
   * 从所有依赖项的dep列表中将自己移除
   */
  teardown() {
    let i = this.deps.length
    while (i--) {
      this.deps[i].removeSub(this)
    }
  }
}


let uid = 0
export default class Dep {
  constructor() {
    this.subs = []
    this.id = uid++ //新增
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  // removeSub(sub) {
  //   remove(this.subs, sub);
  // }
  removeSub(sub) {
    const index = this.subs.indexOf(sub)
    if (index > -1) {
      return this.subs.splice(index, 1)
    }
  }
  depend() {
    if (window.target) {
      // this.addSub(window.target);   //废弃
      window.target.addDep(this)  //新增
    }
  }
  notify() {
    const subs = this.subs.slice()
    for (let i = 0; i < subs.length; i++) {
      subs[i].update();
    }
  }
}
